﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
#if SILVERLIGHT
using AbstraX.ClientInterfaces;
#else
using AbstraX.ServerInterfaces;
#endif
using System.Reflection;
using System.Runtime.Serialization;
using System.ComponentModel.DataAnnotations;
using AbstraX.XPathBuilder;
using System.Linq;
using System.Diagnostics;
using System.ServiceModel.DomainServices.Server;
using MvvmTreeView;
using AbstraX.TypeMappings;
using System.Collections.Generic;
using System.Linq.Expressions;
using AbstraX;
using AbstraX.Templates;
using AbstraX.AssemblyInterfaces;
using AbstraX.Contracts;

namespace AssemblyProvider.Web.Entities
{
    [DataContract, NodeImage("AssemblyProvider.Web.Images.Type.png"), ClientCodeGeneration(typeof(AbstraXClientInterfaceGenerator))]
    public class AssemblyType : IElement, IAssemblyType
    {
        private System.Type baseType;
        private IBase parent;
        private string queryWhereProperty;
        private object queryWhereValue;
        private string imageURL;
        private float childOrdinal;
        private DefinitionKind kind;

        public AssemblyType()
        {
        }

        public AssemblyType(System.Type baseType, IBase parent)
        {
            this.baseType = baseType;
            this.parent = parent;

            if (parent is Assembly)
            {
                if (baseType.Namespace != null && baseType.Namespace.Length > 0)
                {
                    this.FolderKeyPair = baseType.Namespace + ";AssemblyProvider.Web.Images.Namespace.png";
                }
            }
            else if (parent is AssemblyType)
            {
                this.FolderKeyPair = "Base Types;AssemblyProvider.Web.Images.Folder.png";
            }

            if (baseType.BaseType != null && (baseType.BaseType.FullName == "System.MulticastDelegate" || baseType.BaseType.FullName == "System.Delegate"))
            {
                imageURL = "AssemblyProvider.Web.Images.Delegate.png";
                this.childOrdinal = 5.0f;

                kind = DefinitionKind.Delegate;
            }
            else if (baseType.IsEnum)
            {
                imageURL = "AssemblyProvider.Web.Images.Enum.png";
                this.childOrdinal = 4.0f;

                kind = DefinitionKind.Enumeration;
            }
            else if (baseType.IsInterface)
            {
                imageURL = "AssemblyProvider.Web.Images.Interface.png";
                this.childOrdinal = 3.0f;

                kind = DefinitionKind.Interface;
            }
            else if (!baseType.IsClass)
            {
                imageURL = "AssemblyProvider.Web.Images.Struct.png";
                this.childOrdinal = 2.0f;

                kind = DefinitionKind.Class;
            }
            else
            {
                imageURL = string.Empty;
                this.childOrdinal = 1.0f;

                kind = DefinitionKind.Structure;
            }
        }

        public AssemblyType(BaseType baseType, IBase parent)
        {
            try
            {
                this.baseType = System.Type.GetType(baseType.FullyQualifiedName, true);
                this.parent = parent;
            }
            catch (Exception ex)
            {
                Debugger.Break();
            }
        }

        [DataMember]
        public float ChildOrdinal
        {
            get
            {
                return childOrdinal;
            }
        }

        [DataMember]
        public string DebugInfo
        {
            get
            {
                return this.Name + ID;
            }
        }

        [DataMember, Key]
        public string ID
        {
            get
            {
                return this.MakeID("Type='" + this.baseType.FullName + "'");
            }
        }

        [DataMember]
        public bool IsContainer
        {
            get 
            {
                return false;
            }
        }

        [Include, Association("Parent_BaseType", "ID", "ParentID")]
        public BaseType DataType
        {
            get
            {
                return new BaseType
                {
                    FullyQualifiedName = baseType.AssemblyQualifiedName,
                    Name = baseType.Name,
                    ID = baseType.FullName,
                    ParentID = this.ID,
                };
            }
        }

        [Association("Type_FieldAttributes", "ID", "ParentID")]
        public List<FieldAttribute> FieldAttributes
        {
            get
            {
                var attributes = new List<FieldAttribute>();

                if (queryWhereProperty != null && queryWhereProperty == "Field")
                {
                    var field = (FieldInfo)baseType.GetFields().Where(p => p.Name == (string)queryWhereValue && p.IsPublic).Single();

                    attributes.Add(new FieldAttribute(field, this));
                }
                else
                {
                    foreach (FieldInfo field in baseType.GetFields())
                    {
                        attributes.Add(new FieldAttribute(field, this));
                    }
                }

                return attributes;
            }
        }

        [Association("Type_PropertyAttributes", "ID", "ParentID")]
        public List<PropertyAttribute> PropertyAttributes
        {
            get
            {
                var attributes = new List<PropertyAttribute>();

                if (queryWhereProperty != null && queryWhereProperty == "Property")
                {
                    var property = (PropertyInfo)baseType.GetProperties().Where(p => p.Name == (string)queryWhereValue).Single();

                    attributes.Add(new PropertyAttribute(property, this));
                }
                else
                {
                    foreach (PropertyInfo property in baseType.GetProperties())
                    {
                        if (property.PropertyType.IsPrimitive || AttributeType.Types.Contains(property.PropertyType))
                        {
                            attributes.Add(new PropertyAttribute(property, this));
                        }
                    }
                }

                return attributes;
            }
        }

        public IEnumerable<IAttribute> Attributes
        {
            get 
            {
                var attributes = Queryable.Union(this.PropertyAttributes.AsQueryable().Cast<IAttribute>().Select(a => a),
                    this.FieldAttributes.AsQueryable().Cast<IAttribute>().Select (a => a));

                return attributes;
            }
        }

        [Association("Type_Types", "ID", "ParentID")]
        public List<AssemblyType> BaseTypes
        {
            get
            {
                var types = new List<AssemblyType>();

                if (baseType.BaseType != null)
                {
                    types.Add(new AssemblyType(baseType.BaseType, this));
                }

                baseType.GetInterfaces().ToList().ForEach(t => types.Add(new AssemblyType(t, this)));

                return types;
            }
        }

        [Association("Type_PropertyElements", "ID", "ParentID")]
        public List<PropertyElement> PropertyElements
        {
            get
            {
                var elements = new List<PropertyElement>();

                if (queryWhereProperty != null && queryWhereProperty == "Property")
                {
                    var property = (PropertyInfo)baseType.GetProperties().Where(p => p.Name == (string)queryWhereValue).Single();

                    elements.Add(new PropertyElement(property, this));
                }
                else
                {
                    foreach (PropertyInfo property in baseType.GetProperties())
                    {
                        if (!property.PropertyType.IsPrimitive && !AttributeType.Types.Contains(property.PropertyType))
                        {
                            elements.Add(new PropertyElement(property, this));
                        }
                    }
                }

                return elements;
            }
        }

        public IEnumerable<IElement> ChildElements
        {
            get 
            {
                var elements = Queryable.Union(this.PropertyElements.AsQueryable().Cast<IElement>(), 
                    this.BaseTypes.AsQueryable().Cast<IElement>());

                return elements;
            }
        }

        [Association("Type_MethodsConstructors", "ID", "ParentID")]
        public List<Operation> MethodsConstructors
        {
            get
            {
                var methodsConstructors = new List<Operation>();

                foreach (MethodInfo method in baseType.GetMethods())
                {
                    if (!method.Name.StartsWith("get_") && !method.Name.StartsWith("set_") && !method.Name.StartsWith("add_") && !method.Name.StartsWith("remove_"))
                    {
                        methodsConstructors.Add(new MethodOperation(method, this));
                    }
                }

                foreach (ConstructorInfo constructor in baseType.GetConstructors())
                {
                    methodsConstructors.Add(new ConstructorOperation(constructor, this));
                }

                return methodsConstructors;
            }
        }

        public IEnumerable<IOperation> Operations
        {
            get
            {
                var operations = this.MethodsConstructors.AsQueryable().Cast<IOperation>().Select(o => o);

                return operations;
            }
        }

        [DataMember]
        public string ParentID
        {
            get 
            {
                return this.Parent.ID;
            }
        }

        [DataMember]
        public string Name
        {
            get 
            {
                var typeName = baseType.GenerateTypeString();

                return typeName;
            }
        }

        [DataMember]
        public string Description
        {
            get
            {
                return string.Empty;
            }
        }

        [DataMember]
        public string Documentation
        {
            get
            {
                return string.Empty;
            }
        }

        [DataMember]
        public string ImageURL
        {
            get
            {
                return imageURL;
            }
        }

        [DataMember]
        public IBase Parent
        {
            get 
            {
                return parent;
            }
        }

        [DataMember]
        public string FolderKeyPair { get; set; }

        public void ExecuteWhere(string property, object value)
        {
            if (value is XPathAxisElement && (property == "ID" || property == "ParentID"))
            {
                var predicate = ((XPathAxisElement)value).Predicates.First();

                queryWhereProperty = predicate.Left;
                queryWhereValue = predicate.Right;
            }
            else
            {
                Debugger.Break();

                queryWhereProperty = property;
                queryWhereValue = value;
            }
        }

        public void ExecuteWhere(Expression expression)
        {
        }

        public void ExecuteWhere(XPathAxisElement element)
        {
            var predicate = element.Predicates.First();

            queryWhereProperty = predicate.Left;
            queryWhereValue = predicate.Right;
        }

        public void ClearPredicates()
        {
            queryWhereProperty = null;
            queryWhereValue = null;
        }

        public IRoot Root
        {
            get
            {
                IBase baseObject = this;

                while (baseObject != null)
                {
                    baseObject = baseObject.Parent;

                    if (baseObject is IRoot)
                    {
                        return (IRoot)baseObject;
                    }
                }

                return null;
            }
        }

        [DataMember]
        public DefinitionKind Kind
        {
            get
            {
                return kind;
            }
        }

        [DataMember]
        public bool HasChildren
        {
            get 
            {
                if (baseType.GetProperties().Any())
                {
                    return true;
                }
                else if (baseType.GetMethods().Any())
                {
                    return true;
                }
                else if (baseType.GetConstructors().Any())
                {
                    return true;
                }

                return false;
            }
        }

        IEnumerable<IAssemblyType> IAssemblyType.BaseTypes
        {
            get
            {
                foreach (var type in this.BaseTypes)
                {
                    yield return type;
                }
            }
        }

        IEnumerable<IFieldAttribute> IAssemblyType.FieldAttributes
        {
            get
            {
                foreach (var attribute in this.FieldAttributes)
                {
                    yield return attribute;
                }
            }
        }

        IEnumerable<IOperation> IAssemblyType.MethodsConstructors
        {
            get
            {
                foreach (var methodConstructor in this.MethodsConstructors)
                {
                    yield return methodConstructor;
                }
            }
        }

        IEnumerable<IPropertyAttribute> IAssemblyType.PropertyAttributes
        {
            get
            {
                foreach (var attribute in this.PropertyAttributes)
                {
                    yield return attribute;
                }
            }
        }

        IEnumerable<IPropertyElement> IAssemblyType.PropertyElements
        {
            get
            {
                foreach (var element in this.PropertyElements)
                {
                    yield return element;
                }
            }
        }

        [DataMember, Include, Association("Parent_Facet", "ID", "ParentID")]
        public Facet[] Facets
        {
            get
            {
                return null;
            }
        }

        [Exclude]
        public Type SystemType
        {
            get 
            {
                return baseType;
            }
        }

        [Exclude]
        public ContainerType AllowableContainerTypes
        {
            get { throw new NotImplementedException(); }
        }

        [Exclude]
        public ConstructType AllowableConstructTypes
        {
            get { throw new NotImplementedException(); }
        }

        [Exclude]
        public ContainerType DefaultContainerType
        {
            get { throw new NotImplementedException(); }
        }

        [Exclude]
        public ConstructType DefaultConstructType
        {
            get { throw new NotImplementedException(); }
        }
    }
}
